package cn.serein.charging.device.startup;


import cn.serein.charging.device.constants.Constants;
import cn.serein.charging.device.dao.repository.StationRepository;
import cn.serein.charging.device.pojo.StationPO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.core.BoundGeoOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Component
public class StationDataInit implements ApplicationRunner {
    /**
     * 在项目启动前执行一些业务
     * 可以使用@PostConstruct注解  速度很快  但是不推荐
     * 推荐使用  实现 applicationRunner接口
     */

    // @PostConstruct
    @PostConstruct
    public void init() {
        log.debug("使用@PostConstruct注解实现在服务启动前去数据库里面获取场站的信息");
    }

    @Autowired
    private StationRepository stationRepository;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedisTemplate<String,Object> redisTemplateHash;

    @Autowired
    private RedisTemplate<String,StationPO> redisTemplateHashAll;


    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.debug("使用实现 applicationRunner接口实现在服务启动前去数据库里面获取场站的信息");
        
        log.debug("从数据库里面获取所有的场站信息");
        List<StationPO> stationPOList = stationRepository.getAllStation();
        log.debug("获取数据存入到redis中");

        // 判断是否为空  为空需要报警
        if(!CollectionUtils.isEmpty(stationPOList)){

            /**
             * TODO  数据库批量处理
             */
            // 先使用循环去实现
            for(StationPO stationPO : stationPOList){
                // 存基本信息
                /**
                 * TODO  Hash实现  通过大Map存储信息
                 * Map<String,Map<String,Object>>
                 *     使用map  先去设置序列化器
                 *
                 */
                saveGeoByGEOAndInfoByRedisHashALL(stationPO);

                /**
                 * TODO  Hash实现
                 * Map<String,Map<String,Object>>
                 *     使用map  先去设置序列化器
                 *     第一个参数  使用站id   第二个参数 可以使用站的属性  比如name   第三个参数存储站的数据
                 *     通过小map中拿到设置的Key  存储站的基本信息
                 */
                saveGeoByGEOAndInfoByRedisHash(stationPO);
                /**
                 * 通过redis中的String实现
                 * 先绑定  再通过opsForValue()方法  设置key
                 * TODO  String实现
                 * Map<String,String>
                 */
                saveGeoByGEOAndInfoByString(stationPO);
                // 存位置信息
                /**
                 * TODO  GEO实现
                 * 通过redis中的GEO去获取
                 */
                saveStationGEO(stationPO);
            }
        }else{
            log.error("从数据库里面获取场站信息为空");
        }
        
        /*// 存基本信息
        saveStationInfo(stationPOList);
        // 存位置信息
        saveStationGEO(stationPOList);*/
        
        

    }

    private void saveGeoByGEOAndInfoByRedisHashALL(StationPO stationPO) {
        /**
         * 通过设置全局的key来做大map的key
         * 站的id 当作小map的key
         * po 当作小map的value值
         */
        String key = Constants.STATION_INFO_HASH_ALL_KEY;

        HashOperations<String,String,StationPO> hashOperations =
                redisTemplateHashAll.opsForHash();

        Map<String,StationPO> map = new HashMap<>();
        map.put(stationPO.getId().toString(),stationPO);

        hashOperations.putAll(key,map);
    }

    private void saveGeoByGEOAndInfoByRedisHash(StationPO stationPO) {

        //
        String key = Constants.STATION_INFO_HASH_KEY + stationPO.getId();

        HashOperations<String,String,Object> hashOperations = redisTemplateHash.opsForHash();

        // 声明小map
        Map<String,Object> map = new HashMap<>();

        // 这里只设置了站名  那么就将站的基本信息存在对应的map之中
        map.put("stationName",stationPO.getStationName());

        hashOperations.putAll(key,map);

    }

    private void saveGeoByGEOAndInfoByString(StationPO stationPO) {
        saveStationGEOByGEO(stationPO);
        // 下面这个方法用于设置key  stationId
        saveStationInfoByRedisString(stationPO);
    }

    private void saveStationInfoByRedisString(StationPO stationPO) {
        // 设置key
        String key = Constants.STATION_INFO_KEY + stationPO.getId();

        redisTemplate.boundValueOps(key).set(stationPO);
    }


    private void saveStationGEOByGEO(StationPO stationPO) {
        // 先绑定
        BoundGeoOperations stations = redisTemplate.boundGeoOps(Constants.REDIS_STRING_STATION_MEMBER);

        // 获取经纬度
        BigDecimal stationLng = stationPO.getStationLng();
        BigDecimal stationLat = stationPO.getStationLat();

        // 通过坐标构建点
        Point point = new Point(
                Double.parseDouble(stationLng.toEngineeringString()),
                Double.valueOf(stationLat.toEngineeringString()));

        // 通过redisTemplate的add方法  通过 string 的话  传递站的id
        // 目的是是将站的基本信息和坐标信息绑定到一起
        stations.add(point,stationPO.getId());
    }


    private void saveStationGEO(StationPO stationPO) {
        // 先绑定
        BoundGeoOperations stations = redisTemplate.boundGeoOps(Constants.REDIS_GEO_STATION_MEMBER);

        // 获取经纬度
        BigDecimal stationLng = stationPO.getStationLng();
        BigDecimal stationLat = stationPO.getStationLat();

        // 通过坐标构建点
        Point point = new Point(
                Double.parseDouble(stationLng.toEngineeringString()),
                Double.valueOf(stationLat.toEngineeringString()));

        // 通过redisTemplate的add方法
        stations.add(point,stationPO);

    }

}
